import time
import pandas as pd
import numpy as np
from scipy import stats
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn import metrics
from sklearn.cluster import KMeans
from sklearn.metrics import classification_report
from sklearn.metrics import precision_recall_fscore_support, classification_report, confusion_matrix
from sklearn.model_selection import cross_val_score
from sklearn.utils.multiclass import unique_labels
from sklearn.metrics import accuracy_score
import sklearn
from sklearn.linear_model import LinearRegression
from sklearn import datasets, linear_model
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn import svm as class_svm
from sklearn import preprocessing
from sklearn.neural_network import MLPClassifier as mlp
from sklearn.model_selection import train_test_split
import itertools
import warnings
warnings.filterwarnings('ignore')
def lerCSV(caminho_arquivo, header):
return pd.read_csv(caminho_arquivo, index_col=False, header=header, squeeze=True)
def plot_confusion_matrix(y_true, y_pred, classes,
normalize=False,
title=None,
cmap=plt.cm.Blues):
if not title:
if normalize:
title = 'Matriz de Confusão normalizada'
else:
title = 'Matriz de Confusão NÃO normalizada'
# calcula a matriz de confusão
cm = confusion_matrix(y_true, y_pred)
classes = classes[unique_labels(y_true, y_pred)]
if normalize:
cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
print("Matriz de Confusão normalizada")
else:
print('Matriz de Confusão NÃO normalizada')
#print(cm)
fig, ax = plt.subplots()
im = ax.imshow(cm, interpolation='nearest', cmap=cmap)
ax.figure.colorbar(im, ax=ax)
ax.set(xticks=np.arange(cm.shape[1]),
yticks=np.arange(cm.shape[0]),
# ...criando os labels
xticklabels=classes, yticklabels=classes,
title=title,
ylabel='Entrada',
xlabel='Saida')
# rotacionando os labels
plt.setp(ax.get_xticklabels(), rotation=45, ha="right",
rotation_mode="anchor")
# Percorrendo sobre os dados e criando as anotações de texto
fmt = '.2f' if normalize else 'd'
thresh = cm.max() / 2.
for i in range(cm.shape[0]):
for j in range(cm.shape[1]):
ax.text(j, i, format(cm[i, j], fmt),
ha="center", va="center",
color="white" if cm[i, j] > thresh else "black")
fig.tight_layout()
return ax
df = lerCSV("Dados-Prova02.csv", header=0)
df.head(3)
len(df)
def imprimirRelacionamentosChurn(df):
qtd_registros = df['Churn'].count()
qtd_cancelamentos = df[df['Churn'] == 1]['Churn'].count()
qtd_ativos = df[df['Churn'] == 0]['Churn'].count()
p_cancelamentos = (qtd_cancelamentos/qtd_registros*100)
p_ativos = (qtd_ativos/qtd_registros*100)
print(' Quantidade de registros:', qtd_registros)
print(' Número de cancelamento de contratos:', qtd_cancelamentos)
print(' Número de contratos não cancelados:', qtd_ativos)
print(' Porcentagem de contratos cancelados: %.2f%%' % p_cancelamentos)
print(' Porcentagem de contratos não cancelados: %.2f%%' % p_ativos)
return p_cancelamentos, p_ativos
def imprimirRelacionamentosChurnAux(df_convertido, coluna, titulo_0='Valor = 1', titulo_1='Valor = 0'):
x = coluna
df_aux = df_convertido[[x, 'Churn']]
print('----- Atributo:', coluna, '-----')
ax = sns.countplot(data=df_aux, x=x, hue='Churn')
for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='center', fontsize=11, color='gray', rotation=45, xytext=(0, 20),
textcoords='offset points')
print(titulo_0)
p_c1, p_a1 = imprimirRelacionamentosChurn(df_aux[df_aux[x]==0])
print('\n'+titulo_1)
p_c2, p_a2 = imprimirRelacionamentosChurn(df_aux[df_aux[x]==1])
print('\nDiferença das porcentagens de contratos cancelados: %.2f%%' % (abs(p_c1 - p_c2)))
print('Diferença das porcentagens de contratos não cancelados: %.2f%%' % (abs(p_a1 - p_a2)))
plt.show()
print(*df.columns, sep=', ')
Verificando os tipos de dados dos atributos.
print(df.dtypes)
Verificando se existem dados nulos.
print('Quantidade de registros nulos:', df.isnull().sum().max())
for col in df.columns:
print(col, sum(pd.isnull(df[col])))
Verificando os valores únicos de cada coluna.
for col in df.columns:
print(col, df[col].unique())
Convertendo algumas colunas para tipos numéricos: Yes = 1; No = 0 e também Male = 1, Female = 0
df_convertido = df.copy()
df_convertido['sexo'] = [1 if x == 'Male' else 0 for x in df_convertido['sexo']]
cols_conversao = ['has-parceiro', 'has-dependentes', 'has-servico-telefone', 'has-multiplas-linhas',
'has-seguranca-online', 'has-backup-online', 'has-protecao-dispositivo',
'has-suporte-tecnico', 'has-streaming-tv', 'has-streaming-filmes',
'has-pagamento-online', 'Churn']
for col in cols_conversao:
df_convertido[col] = [1 if x == 'Yes' else 0 for x in df_convertido[col]]
#df_convertido['tipo-contrato'] = [0 if x == 'Month-to-month' else 1 if x == 'One year' else 2]
df_convertido.head(3)
print(df_convertido.dtypes)
#Usando a correlação de Pearson
plt.figure(figsize=(15,8))
corr = df_convertido.corr()
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.show()
Imprimindo pairplots. O primeiro consiste dos atributos com maiores correlações apresentadas e o segundo consiste de outros atributos também interessantes para a análise.
cols = ['Churn', 'has-streaming-tv', 'has-protecao-dispositivo', 'has-streaming-filmes', 'has-backup-online',
'tempo-contrato-meses', 'valor-mensal']
sns.set(style='whitegrid')
sns.pairplot(df_convertido[cols], hue='Churn', palette=['blue', 'red'])
plt.show()
Estudando maiores detalhes a respeito de correlações de alguns atributos binários.
for col in df_convertido[cols].drop(['Churn', 'tempo-contrato-meses', 'valor-mensal'], axis=1):
imprimirRelacionamentosChurnAux(df_convertido, col)
cols = ['Churn', 'sexo', 'is-idoso', 'has-dependentes', 'has-parceiro',
'has-servico-telefone', 'has-seguranca-online', 'has-pagamento-online']
sns.set(style='whitegrid')
sns.pairplot(df_convertido[cols], hue='Churn', palette=['blue', 'red'])
plt.show()
for col in df_convertido[cols].drop(['Churn'], axis=1):
imprimirRelacionamentosChurnAux(df_convertido, col)
Verificando se existe correlação entre o método de pagamento e o atributo Churn. (metodo-pagamento ['Electronic check' 'Mailed check' 'Bank transfer (automatic)' 'Credit card (automatic)'])
plt.figure(figsize=(10,5))
ax = sns.countplot(x="Churn", hue="metodo-pagamento", data=df_convertido, palette=['red', 'blue', 'blue', 'blue']);
ax.set_ylabel('Número de clientes que cancelaram contratos', fontsize = 12)
ax.set_xlabel('Churn', fontsize = 12)
# alinhando tÃtulo para não prejudicar visualização dos valores
ax.set_title('Churn por método de pagamento', fontsize=15, horizontalalignment='left')
for p in ax.patches:
ax.annotate("%.2f" % p.get_height(), (p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='center', fontsize=11, color='gray', rotation=45, xytext=(0, 20),
textcoords='offset points')
plt.show()
Observando-se o pairplot, a matriz de correlação e as outras informações mostradas acima, podemos observar o seguinte:
df_convertido2 = df_convertido.copy()
#tipo-contrato ['Month-to-month' 'One year' 'Two year']
df_convertido2['tipo-contrato'] = [0 if x == 'Month-to-month'
else 1 if x == 'One year' else 2 for x in df_convertido2['tipo-contrato']]
#servico-internet ['DSL' 'Fiber optic' 'No']
df_convertido2['servico-internet'] = [0 if x == 'DSL' else 1 if x == 'Fiber optic' else 2
for x in df_convertido2['servico-internet']]
#metodo-pagamento ['Electronic check' 'Mailed check' 'Bank transfer (automatic)'
# 'Credit card (automatic)']
df_convertido2['metodo-pagamento'] = [0 if x == 'Electronic check' else 1 if x == 'Mailed check'
else 2 if x == 'Bank transfer (automatic' else 3
for x in df_convertido2['metodo-pagamento']]
def calcLinearRegression(x, y, cols, y_label):
lm = LinearRegression()
lm.fit(x, y)
#print('Coeficiente estimado:', lm.coef_)
#print('Valor de b (lm.intercept_): ', lm.intercept_)
print('R2 (score):', lm.score(x, y))
#print('1 - R2 (score):', 1 - lm.score(x, y))
print('Equação linear: ', y_label, '= ', end='')
i = 0
for a in lm.coef_:
print(a, "*", cols[i], "+", end='')
i+=1
print('', lm.intercept_)
return lm.coef_, lm.intercept_
cols = df_convertido.drop(['id-usuario', 'valor-mensal'], axis=1).columns
y_label = 'valor-mensal'
for c in cols:
x=df_convertido2[c]
y=df_convertido2[y_label]
try:
a, b = calcLinearRegression(x.values.reshape(-1,1), y, [c], y_label)
except: # isso existe por conta de um bug da biblioteca LinearRegression com o uso de Matplotlib...
a, b = calcLinearRegression(x.values.reshape(-1,1), y, [c], y_label)
r = a * df_convertido2[[c]]
r = r.sum(axis=1) + b
df_convertido2['valor-mensal-predicted'] = r
print('Menor erro de predição:', min(abs(df_convertido2['valor-mensal'] - df_convertido2['valor-mensal-predicted'])))
print('Maior erro de predição:', max(abs(df_convertido2['valor-mensal'] - df_convertido2['valor-mensal-predicted'])))
print('\n')
print()
c = cols
x=df_convertido2[c]
y=df_convertido2[y_label]
a, b = calcLinearRegression(x, y, c, y_label)
r = a * df_convertido2[c]
r = r.sum(axis=1) + b
df_convertido2['valor-mensal-predicted'] = r
df_convertido2['diff-valor-mensal-predicted'] = abs(df_convertido2['valor-mensal'] -
df_convertido2['valor-mensal-predicted'])
print('\nResultado da função describe() para a coluna das diferenças dos valores mensais reais e preditos')
df_convertido2['diff-valor-mensal-predicted'].describe()
c = ['has-streaming-filmes', 'has-streaming-tv', 'has-protecao-dispositivo',
'has-backup-online', 'has-multiplas-linhas']
x=df_convertido2[c]
y=df_convertido2[y_label]
a, b = calcLinearRegression(x, y, c, y_label)
r = a * df_convertido2[c]
r = r.sum(axis=1) + b
df_convertido2['valor-mensal-predicted'] = r
df_convertido2['diff-valor-mensal-predicted'] = abs(df_convertido2['valor-mensal'] -
df_convertido2['valor-mensal-predicted'])
print('\nResultado da função describe() para a coluna das diferenças dos valores mensais reais e preditos')
df_convertido2['diff-valor-mensal-predicted'].describe()
A melhor combinação que dá um valor mais alto para o coeficiente de determinação R2 é quando envolve todos os outros atributos, com R2 = 0.81. Se tratando de uma análise individual, o que possui o maior valor para R2 é has-streaming-tv.
Ainda, predizendo-se os valores em uma nova coluna 'valor-mensal-predicted', e fazendo-se a subtração do valor real, o que obteve um menor erro de predição, isto é, diferença entre valor real e valor predito, foi a abordagem com todas as colunas, com exceção de id-usuario, sexo e valor-mensal.
Vale destacar ainda, que uma outra combinação interessante consiste dos atributos has-streaming-filmes, has-streaming-tv, has-protecao-dispositivo, has-backup-online e has-multiplas-linhas, com um R2 equivalente a 0.65 e com a média de erros e valor de maior erro aceitáveis.
def getClassificadorSVM(x_treino, y_treino, kernel='linear', C=0.01, gamma='scale'):
# C = parâmetro de rugalirazação (default = 1.0)
# gamma = parâmetro para rbf, poly e sigmoid
# degree é apenas para kernel='poly' e seu default é 3 (está indicado apenas por indicar mesmo)
classificador = class_svm.SVC(kernel=kernel, C=C, gamma=gamma, degree=3)
classificador.fit(x_treino, y_treino)
return classificador
def getClassificadorMLP(x_treino, y_treino, solver="adam", alpha=0.0001):
classificador = mlp(solver=solver, alpha=alpha)
classificador.fit(x_treino, y_treino)
return classificador
def getClassificadorKNeighbors(x_treino, y_treino):
classificador = KNeighborsClassifier()
classificador.fit(x_treino, y_treino)
return classificador
def imprimirValidacaoCruzada(classificador, x_teste, y_teste, cv=5):
scores = cross_val_score(classificador, x_teste, y_teste, cv=cv)
print('\n--- Validação Cruzada ---')
print('Acurácia: %.5f (+/- %.5f)' % (scores.mean(), scores.std() * 2))
def mostrarResultadosPrecisao(classificador, x_teste, y_teste, classes):
y_pred = classificador.predict(x_teste)
np.set_printoptions(precision=2)
print('Score: ', classificador.score(x_teste, y_teste))
plot_confusion_matrix(y_teste, y_pred, classes=np.array(classes),title='Matriz de Confusão')
plt.show()
print(classification_report(y_teste,y_pred))
precisao=100*accuracy_score(y_teste,y_pred)
print(f'Precisão do modelo: {precisao:.3f} %')
imprimirValidacaoCruzada(classificador, x_teste, y_teste)
def executar(data, labels, classes, test_size=0.25):
#dividindo o pacote de dados em treino e teste
test_size = test_size
x_treino, x_teste, y_treino, y_teste = train_test_split(data, labels, random_state=0, test_size=test_size)
x_treino = preprocessing.scale(x_treino) # scaling...
x_teste = preprocessing.scale(x_teste)
print('Test size: ' + str(test_size))
print('Quantidade de dados para treino:', round(((1-test_size)*len(df_convertido2))))
print('Quantidade de dados para teste:', round((test_size*len(df_convertido2))))
print('\n--------------- Classificador SVM, kernel linear ---------------\n')
t0 = time.time()
classificador_svm = getClassificadorSVM(x_treino, y_treino, kernel='linear', C=1.0, gamma='scale')
print('Tempo decorrido (treino): %.2f segundos' % (time.time() - t0))
t0 = time.time()
mostrarResultadosPrecisao(classificador_svm, x_teste, y_teste, classes)
print('Tempo decorrido (resultados): %.2f segundos' % (time.time() - t0))
print('\n\n')
print('\n--------------- Classificador MLP, solver adam ---------------\n')
t0 = time.time()
classificador_mlp = getClassificadorMLP(x_treino, y_treino, solver="adam", alpha=0.0001)
print('Tempo decorrido (treino): %.2f segundos' % (time.time() - t0))
t0 = time.time()
mostrarResultadosPrecisao(classificador_mlp, x_teste, y_teste, classes)
print('Tempo decorrido: %.2f segundos' % (time.time() - t0))
print('\n\n')
print('\n--------------- Classificador KNeighbors ---------------\n')
t0 = time.time()
classificador_kneighbors = getClassificadorKNeighbors(x_treino, y_treino)
print('Tempo decorrido: %.2f segundos' % (time.time() - t0))
t0 = time.time()
mostrarResultadosPrecisao(classificador_kneighbors, x_teste, y_teste, classes)
print('Tempo decorrido: %.2f segundos' % (time.time() - t0))
data = df_convertido2[['is-idoso', 'has-parceiro', 'has-dependentes',
'tempo-contrato-meses', 'has-seguranca-online',
'has-suporte-tecnico', 'has-pagamento-online',
'valor-mensal']]
labels = df_convertido2['Churn'].values
classes = np.unique(labels)
executar(data, labels, classes, test_size=0.2)
data = df_convertido2[['is-idoso', 'has-parceiro', 'has-dependentes',
'tempo-contrato-meses', 'has-seguranca-online',
'has-suporte-tecnico', 'has-pagamento-online',
'valor-mensal', 'metodo-pagamento']]
executar(data, labels, classes, test_size=0.2)
#Gerar todas as combinações possÃveis para analisar o
#comportamento dos modelo
def get_combinations(values):
comb=[]
for i in range(2, len(values)+1):
comb_with_i_values = [list(v) for v in itertools.combinations(values, i)]
comb.extend(comb_with_i_values)
return comb
def print_labels(model, df, precip_type):
plt.figure(figsize=(10,3))
#Real
plt.subplot(1,2,1)
sns.scatterplot(x=df['tempo-contrato-meses'], y=df['valor-mensal'], hue=precip_type, s=40)
plt.title('Classificação Real')
#K-Means
plt.subplot(1,2,2)
sns.scatterplot(x=df['tempo-contrato-meses'], y=df['valor-mensal'], hue=model.labels_, s=40)
plt.title('Classificação K-Means')
plt.show()
df_convertido3 = df_convertido2.copy()
df_convertido3 = df_convertido3.drop('id-usuario', axis=1)
for features in get_combinations(['valor-mensal', 'has-pagamento-online',
'tempo-contrato-meses', 'is-idoso', 'metodo-pagamento']):
print('Features: ', end='')
print(*features, sep=', ')
kmeans = KMeans(n_clusters=3).fit(df_convertido3[features])
print('Gráficos')
print_labels(kmeans, df_convertido3, labels)
print('Silhueta:', round(metrics.silhouette_score(df_convertido3, kmeans.labels_), 4), '\n\n')
y_pred = pd.Series(kmeans.labels_)
print(classification_report(labels, y_pred))